package helper

import (
	"Hypnos-Soundcore/constants"
	"github.com/pborman/uuid"
	"github.com/pquerna/ffjson/ffjson"
	"github.com/uniplaces/carbon"
	"reflect"
	"strconv"
	"strings"
	"time"
)

// default time format
const (
	LongTimeFormat       = "2006-01-02 15:04:05.000"
	LongTimeSecondFormat = "2006-01-02 15:04:05"
	ShortTimeFormat      = "2006-01-02"
	ShortTimeFormat2     = "20060102"
	ShortTimeFormat3     = "20060102150405"
	UnbindTimeFormat     = "2006-01-02 15:04(-07:00)"

	ISO8601_FORMAT_UTC = "2006-01-02T15:04:05.999Z"
)

// TimeToString time to string transformer .
func TimeToString(t *time.Time, fmt string) string {
	var ret string
	if t != nil {
		ret = t.Format(fmt)
	}
	return ret
}

// StringToTime string to time transformer
func StringToTime(s string, fmt string) (*time.Time, error) {
	var ret *time.Time
	var err error
	if len(s) > 0 {
		t, timeErr := time.ParseInLocation(fmt, s, time.UTC)
		if timeErr == nil {
			ret = &t
		} else {
			err = timeErr
		}
	}
	return ret, err
}

// TimeToInt64 time to int64 transformer
func TimeToInt64(t *time.Time) int64 {
	var ret int64
	if t != nil {
		ret = t.UnixNano()
	}
	return ret
}

// Int64ToTime int64 to time transformer
func Int64ToTime(i int64) *time.Time {
	var ret *time.Time
	if i > 0 {
		t := time.Unix(i, 0)
		ret = &t
	}
	return ret
}

// AddDate int64 to time and add date
func AddDate(inTime int64, years, months, days int, toDayEnd bool, anchorTime int64) int64 {
	oldDate := carbon.NewCarbon(time.Unix(inTime, 0))
	newDate := oldDate
	if years != 0 {
		newDate = newDate.AddMonthsNoOverflow(12 * years)
	}
	if months != 0 {
		newDate = newDate.AddMonthsNoOverflow(months)
	}
	if days != 0 {
		newDate = newDate.AddDays(days)
	}
	if days == 0 && (years != 0 || months != 0) {
		if anchorTime > 0 {
			// 根据anchorTime设置day
			anchorDate := carbon.NewCarbon(time.Unix(anchorTime, 0))
			if newDate.LastDayOfMonth().Day() >= anchorDate.Day() {
				newDate.SetDay(anchorDate.Day())
			}
		} else {
			// 根据oldDate设置day
			if oldDate.Day() == oldDate.LastDayOfMonth().Day() {
				newDate.SetDay(newDate.LastDayOfMonth().Day())
			}
		}
	}
	if toDayEnd {
		// transform to 23:59:59 of day
		newDate.SetHour(23)
		newDate.SetMinute(59)
		newDate.SetSecond(59)
	}
	return newDate.Timestamp()
}

// GetNowTime get utc time without millisecond
func GetNowTime() int {
	return (int)(time.Now().Unix())
}

// GetNowTimeV2 get utc time without millisecond
func GetNowTimeV2() int64 {
	return time.Now().Unix()
}

// GetNowUtcTime get utc time with millisecond
func GetNowUtcTime() int64 {
	t := time.Now()
	return TimeToUtc(&t)
}

// GetNowZeroTime get utc zero time without millisecond
func GetNowZeroTime() int {
	now := time.Now()
	return (int)(time.Date(now.Year(), now.Month(), now.Day(),
		0, 0, 0, 0, now.Location()).Unix())
}

// UtcToTime millisecond to time transformer
func UtcToTime(utc int64) *time.Time {
	var ret *time.Time
	if utc > 0 {
		t := time.Unix(0, utc*1e6)
		ret = &t
	}
	return ret
}

// TimeToUtc millisecond to time transformer
func TimeToUtc(t *time.Time) int64 {
	return (t.UnixNano() / 1e6)
}

// StringToUtc string to utc transformer
func StringToUtc(s string, fmt string) (int64, error) {
	var err error
	var t *time.Time
	if t, err = StringToTime(s, fmt); t != nil && err == nil {
		return TimeToUtc(t), err
	}
	return 0, err
}

func StringToUtcSecond(s string, fmt string) (int64, error) {
	var err error
	var t *time.Time
	if t, err = StringToTime(s, fmt); t != nil && err == nil {
		return t.UTC().Unix(), err
	}
	return 0, err
}

// StringToInt string to int transformer
func StringToInt(in string, def int) (ret int, err error) {
	ret = def
	if len(in) > 0 {
		ret, err = strconv.Atoi(in)
		if err != nil {
			ret = def
		}
	}
	return ret, err
}

// StringToInt64 string to int64 transformer
func StringToInt64(in string, def int64) (ret int64, err error) {
	ret = def
	if len(in) > 0 {
		ret, err = strconv.ParseInt(in, 10, 64)
		if err != nil {
			ret = def
		}
	}
	return ret, err
}

// StringToUint64 string to uint64 transformer
func StringToUint64(in string, def uint64) (ret uint64, err error) {
	ret = def
	if len(in) > 0 {
		ret, err = strconv.ParseUint(in, 10, 64)
		if err != nil {
			ret = def
		}
	}
	return ret, err
}

// StringToBool string to bool transformer
func StringToBool(in string, def bool) (ret bool, err error) {
	ret = def
	if len(in) > 0 {
		ret, err = strconv.ParseBool(in)
		if err != nil {
			ret = def
		}
	}
	return ret, err
}

// EncodeObjToJSON 将对象信息转成json字串存储在session中
func EncodeObjToJSON(obj interface{}, enc bool) (ret string, err error) {
	var data []byte
	if data, err = ffjson.Marshal(obj); err == nil && data != nil {
		ret = string(data)
		if !enc {
			return
		}
		//var encData string
		//if encData, err = encrypt.AesEncryptDefault(ret); err == nil && len(encData) > 0 {
		//	ret = encData
		//} else {
		//	ret = ""
		//}
	}
	return
}

// DecodeJSONToObj 将json转成对象信息
func DecodeJSONToObj(src string, obj interface{}, dec bool) (err error) {
	if len(src) > 0 {
		if dec {
			//var decData string
			//if decData, err = encrypt.AesDecryptDefault(src); err == nil && len(decData) > 0 {
			//	src = decData
			//}
		}
		if err == nil {
			err = ffjson.Unmarshal([]byte(src), obj)
		}
	}
	return
}

// ToStructPtr Return a pointer to the supplied struct via interface{}
func ToStructPtr(obj interface{}) interface{} {
	too := reflect.TypeOf(obj)
	if too.Kind() == reflect.Ptr || too.Kind() != reflect.Struct {
		return obj
	}
	// Create a new instance of the underlying type
	vp := reflect.New(too)
	vp.Elem().Set(reflect.ValueOf(obj))
	// Return pointer to obj -- i.e. &obj.(*Type)
	return vp.Interface()
}

// BitShift 按位计算
func BitShift(src int, shift uint) int {
	return ((0xff << shift) & src) >> shift
}

// ReplaceNewline 替换换行符
func ReplaceNewline(src string) string {
	src = strings.Replace(src, "\r\n", "##", -1)
	src = strings.Replace(src, "\n", "##", -1)
	return src
}

// AbsInt64 取绝对值
func AbsInt64(n int64) int64 {
	if n < 0 {
		return -n
	}
	return n
}

// GetPrefixFromEmail 获取邮箱前缀
func GetPrefixFromEmail(email string) (ret string) {
	ret = email
	if len(email) > 0 {
		if names := strings.Split(email, constants.OperatorAt); len(names) > 0 {
			ret = names[0]
		}
	}
	return
}

// UUID .
func UUID() string {
	return uuid.New()
}

// Min .
func Min(x, y int) int {
	if x < y {
		return x
	}
	return y
}

// Format TimeStamp to display format
func FormatTimestamp(timestamp int64, format string) string {
	return time.Unix(timestamp, 992000).Format(format)
}
